package language.monkey.parser

import language.monkey.ast.Identifier
import language.monkey.ast.LetStatement
import language.monkey.ast.Program
import language.monkey.ast.Statement
import language.monkey.laxer.Lexer
import language.monkey.token.Token
import language.monkey.token.TokenType

/**
 * 解析器
 * @param lexer 词法分析器
 */
class Parser(val lexer: Lexer) {

    /**
     * 当前Token
     */
    var curToken: Token? = null

    /**
     * 下一个Token
     *
     * 如果当前Token 无法判断下一步动作，需要 peekToken 做辅助判断
     */
    var peekToken: Token? = null

    /**
     * 存储解析过程中的错误
     */
    var errors: MutableList<String> = mutableListOf()

    init {
        // 初始化 curToken 和 peekToken
        nextToken()
        nextToken()
    }

    /**
     * 获取下一个 token
     */
    private fun nextToken() {
        curToken = peekToken
        peekToken = lexer.nextToken()
    }

    fun parseProgram(): Program {
        val program = Program()

        while (!curTokenIs(TokenType.EOF)) {
            // 解析语句
            val stmt: Statement? = parseStatement()
            if (stmt != null) {
                program.statements.add(stmt)
            }
            // 移动到下一个 token
            nextToken()
        }
        return program
    }

    private fun parseStatement(): Statement? {
        return when (curToken?.type) {
            TokenType.LET -> parseLetStatement()
            else -> null
        }
    }

    private fun parseLetStatement(): LetStatement? {
        val stmt = LetStatement(curToken!!)

        // 读取并判断下一个token是否适合字面量 token
        if (!exceptPeek(TokenType.IDENT)) {
            return null
        }

        // 到这里，当前 Token 已经是 IDENT
        val indent = Identifier(curToken!!, curToken!!.literal)

        stmt.name = indent

        // 读取并判断下一个 Token 是否适合赋值符
        if (!exceptPeek(TokenType.ASSIGN)) {
            return null
        }

        // TODO 跳过表达式解析, 直接判断分号
        while (!curTokenIs(TokenType.SEMICOLON)) {
            nextToken()
        }

        return stmt
    }

    private fun curTokenIs(type: TokenType): Boolean {
        return curToken?.type == type
    }

    private fun peekTokenIs(type: TokenType): Boolean {
        return peekToken?.type == type
    }

    /**
     * 判断下一个token是否是期望的类型，如果是，则移动到下一个token并返回true，否则返回false
     */
    private fun exceptPeek(type: TokenType): Boolean {
        if (peekTokenIs(type)) {
            nextToken()
            return true
        }
        peekError(type)
        return false
    }

    private fun peekError(type: TokenType) {
        errors.add("expected next token to be $type, got ${peekToken?.type} instead")
    }
}